package com.mzm.util.persistency;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.support.JpaDaoSupport;

import dnl.toolbox.api.PaginationInfo;

/**
 * 
 * @author Daniel Orr
 */
@SuppressWarnings("unchecked")
public class GenericJpaDaoImpl<T, ID extends Serializable> extends JpaDaoSupport implements
		GenericDao<T, ID> {

	private Class<?> persistentClass;
	private Logger logger = LoggerFactory.getLogger(getClass());

	public GenericJpaDaoImpl(Class<?> persistentClass) {
		this.persistentClass = persistentClass;
	}

	protected Class<?> getPersistentClass() {
		return persistentClass;
	}

	public void setPersistentClass(Class<?> persistentClass) {
		this.persistentClass = persistentClass;
	}

	@Override
	public T findById(ID id, boolean lock) throws DataAccessException {
		return (T) getJpaTemplate().find(getPersistentClass(), id);
	}

	public T store(T entity) throws DataAccessException {
		return (T) getJpaTemplate().merge(entity);
	}

	@Override
	public void delete(T entity) throws DataAccessException {
		getJpaTemplate().remove(entity);
	}

	@Override
	public List<T> findAll() throws DataAccessException {
		List<T> result = getJpaTemplate().find(
				"SELECT \n" +
				"	t \n" +
				"FROM \n" +
				persistentClass.getName()+" t");
		return result;
	}

	@Override
	public List<T> find(final String queryString, final Object... values){
		List<T> result = getJpaTemplate().find(queryString, values);
		return result;
	}
	
	@Override
	public ID create(T entity) {
		getJpaTemplate().persist(entity);
		return null;
	}

	@Override
	public void createOrUpdate(T entity) {
		getJpaTemplate().persist(entity);
	}

	@Override
	public void delete(ID id) {
		Object obj = findById(id, false);
		getJpaTemplate().remove(obj);
	}

	@Override
	public void deleteAll() {
		getJpaTemplate().execute(new JpaCallback(){
			@Override
			public Object doInJpa(EntityManager em) throws PersistenceException {
				Query query = em.createQuery("DELETE FROM "+persistentClass.getName());
				int rowsAffected = query.executeUpdate();
				if(logger.isDebugEnabled()){
					logger.debug("deleteAll() deleted "+rowsAffected+" rows.");
				}
				return null;
			}
			
		});
	}

	@Override
	public T findUniqueResultByExample(T example) {
		throw new UnsupportedOperationException();
	}

	@Override
	public Collection<T> getPage(final PaginationInfo paginationInfo) {
		return getJpaTemplate().executeFind(new JpaCallback(){
			@Override
			public Object doInJpa(EntityManager em) throws PersistenceException {
				StringBuilder queryStr = new StringBuilder(
						"SELECT \n" +
						"	t \n" +
						"FROM \n" +
						persistentClass.getName()+" t ");
				if(paginationInfo.getOrderBy() != null){
					queryStr.append(" ORDER BY ");
					queryStr.append(paginationInfo.getOrderBy());
					queryStr.append(" ");
					queryStr.append(paginationInfo.isAsc() ? "ASC": "DESC");
				}
				
				Query query = em.createQuery(queryStr.toString());
				query.setFirstResult(paginationInfo.getStartIndex());
				query.setMaxResults(paginationInfo.getPageSize());
				List<T> result = query.getResultList();
				return result;
			}
			
		});
	}

	@Override
	public long count() {
		List<Long> result = getJpaTemplate().find(
				"SELECT COUNT(t.id) \n" +
				"FROM \n" +
				persistentClass.getName()+" t");
		return result.get(0);
	}

	@Override
	public int getTotalNumberOfEntities() {
		throw new UnsupportedOperationException();
	}

	@Override
	public void merge(T entity) {
		getJpaTemplate().merge(entity);
	}

	@Override
	public void refresh(T entity) {
		getJpaTemplate().refresh(entity);
	}

	@Override
	public void update(T entity) {
		getJpaTemplate().persist(entity);
	}

	@Override
	public Collection<T> findByExample(T example) {
		throw new UnsupportedOperationException();
	}

}
